package jp.dodododo.aop.interceptors;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * {@.en }
 * 
 * <br />
 * 
 * {@.ja }
 * 
 * @author Satoshi Kimura
 */
public class ToStringer {

	private static final String FIELD_SEPARATOR = ",";

	private static final String VALUE_SEPARATOR = "=";

	private static final String DOUBLE_QUOTE = "\"";

	private static final String SINGLE_QUOTE = "\'";

	private static final String FIELDS_BEGIN = "[";

	private static final String FIELDS_END = "]";

	private static final String ARRAY_BEGIN = "{";

	private static final String ARRAY_END = "}";

	private static boolean includeConstant;

	private static boolean includeStatic;

	private static String dateFormat = "yyyy/MM/dd HH:mm:ss";

	/**
	 * {@.en }
	 * 
	 * <br />
	 * 
	 * {@.ja 定数を出力するかどうかを設定します。}
	 * 
	 * @param includeConstant
	 */
	public static void setIncludeConstant(boolean includeConstant) {
		ToStringer.includeConstant = includeConstant;
	}

	/**
	 * {@.en }
	 * 
	 * <br />
	 * 
	 * {@.ja staticなfieldを出力するかどうかを設定します。}
	 * 
	 * @param includeStatic
	 */
	public static void setIncludeStatic(boolean includeStatic) {
		ToStringer.includeStatic = includeStatic;
	}

	/**
	 * {@.en }
	 * 
	 * <br />
	 * 
	 * {@.ja 日付のフォーマットを設定します。}
	 * 
	 * @param format
	 */
	public static void setDateFormat(String format) {
		dateFormat = format;
	}

	/**
	 * * {@.en }
	 * 
	 * <br />
	 * 
	 * {@.ja }
	 * 
	 * 
	 * @param target
	 * @return
	 */
	public static String toString(Object target) {
		if (target == null) {
			return "null";
		} else {
			try {
				return toString(target.getClass(), target);
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}
	}

	private static String toString(Class<?> targetClass, Object target) throws IllegalAccessException {
		StringBuilder buf = new StringBuilder(100);

		buf.append(targetClass.getSimpleName());
		// buf.append("@");
		// buf.append(Integer.toHexString(System.identityHashCode(target)));

		buf.append(FIELDS_BEGIN);
		boolean hasFields = false;
		while (targetClass != null) {
			Field[] fields = targetClass.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				Field field = fields[i];
				field.setAccessible(true);

				int modifiers = field.getModifiers();
				if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) && !includeConstant) {
					continue;
				}
				if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers) && !includeStatic) {
					continue;
				}

				buf.append(field.getName());
				buf.append(VALUE_SEPARATOR);
				append(buf, field.get(target));
				buf.append(FIELD_SEPARATOR);
				hasFields = true;
			}
			targetClass = targetClass.getSuperclass();
		}
		if (hasFields) {
			buf.setLength(buf.length() - FIELD_SEPARATOR.length());
		}
		buf.append(FIELDS_END);

		return new String(buf);
	}

	public static StringBuilder append(StringBuilder buf, Object value) {
		if (value == null) {
			buf.append((String) null);
			return buf;
		}
		if (value.getClass().isArray()) {
			appendArray(buf, value);
			return buf;
		}
		if (value instanceof String) {
			buf.append(DOUBLE_QUOTE).append(value).append(DOUBLE_QUOTE);
			return buf;
		}
		if (value instanceof Character) {
			buf.append(SINGLE_QUOTE).append(value).append(SINGLE_QUOTE);
			return buf;
		}
		if (value instanceof Date) {
			buf.append(new SimpleDateFormat(dateFormat).format((Date) value));
			return buf;
		}
		buf.append(value);
		return buf;
	}

	private static void appendArray(StringBuilder buf, Object array) {
		buf.append(ARRAY_BEGIN);
		int length = Array.getLength(array);
		for (int i = 0; i < length; i++) {
			append(buf, Array.get(array, i));
			buf.append(FIELD_SEPARATOR);
		}
		if (length > 0) {
			buf.setLength(buf.length() - FIELD_SEPARATOR.length());
		}
		buf.append(ARRAY_END);
	}
}